//
// Created by Shao Wenhan on 2020/11/18.
//

#include <gtest/gtest.h>
#include <vector>
#include "CostAndPenalty.h"
using namespace std;

class TestCostAndPenalty : public testing::Test
{
public:
    CostAndPenalty m_costAndPenalty;
    Gain m_gain;
    CostAndPenalty m_costAndPenaltyAfterSubGain;
    CostAndPenalty m_feasibleCostAndPenalty;
    TestCostAndPenalty(){
        m_gain.costGain = -3;
        m_costAndPenalty.totalCost = 2;
        m_costAndPenaltyAfterSubGain.totalCost = 5;
        m_feasibleCostAndPenalty.totalCost = 5;
        for(auto &penalty : m_feasibleCostAndPenalty.penalties)
            penalty = 0;
#if defined(Cons_duration)
        m_gain.penaltyGains[Constraint::duration] = -1;
        m_costAndPenalty.penalties[Constraint::duration] = 0;
        m_costAndPenaltyAfterSubGain.penalties[duration] = 1;
#endif
#if defined(Cons_tourLength)
        m_gain.penaltyGains[Constraint::tourLength] = -1;
        m_costAndPenalty.penalties[Constraint::tourLength] = 0;
        m_costAndPenaltyAfterSubGain.penalties[tourLength] = 1;
#endif
#if defined(Cons_range)
        m_gain.penaltyGains[Constraint::range] = 2;
        m_costAndPenalty.penalties[Constraint::range] = 2;
        m_costAndPenaltyAfterSubGain.penalties[range] = 0;
#endif
#if defined(Cons_vehicleCap)
        m_gain.penaltyGains[Constraint::vehicleCap] = -3;
        m_costAndPenalty.penalties[Constraint::vehicleCap] = 2;
        m_costAndPenaltyAfterSubGain.penalties[Constraint::vehicleCap] = 5;
#endif
#if defined(Cons_depotCap)
        m_gain.penaltyGains[Constraint::depotCap] = -1;
        m_costAndPenalty.penalties[Constraint::depotCap] = 0;
        m_costAndPenaltyAfterSubGain.penalties[Constraint::depotCap] = 1;
#endif
    }
    void assignCostAndPenalty(CostAndPenalty &costAndPenalty, DisType cost, const vector<DisType> &penalties)
    {
        costAndPenalty.totalCost = cost;
        for(int i = 0; i < Constraint::num; i++)
            costAndPenalty.penalties[i] = penalties[i];
    }

};

TEST_F(TestCostAndPenalty, OverloadOperatorEqual)
{
    ASSERT_EQ(m_costAndPenalty, m_costAndPenalty);
    ASSERT_FALSE(m_costAndPenalty == m_costAndPenaltyAfterSubGain);
}

TEST_F(TestCostAndPenalty, OverloadOperatorSubtractBetweenCostAndPenalty)
{
    ASSERT_EQ(m_gain, m_costAndPenalty-m_costAndPenaltyAfterSubGain);
    ASSERT_FALSE(m_gain == m_costAndPenaltyAfterSubGain-m_costAndPenalty);
}

TEST_F(TestCostAndPenalty, OverloadOperatorSubtractBetweenCostAndPenaltyAndGain)
{
    ASSERT_EQ(m_costAndPenaltyAfterSubGain, m_costAndPenalty-m_gain);
    ASSERT_FALSE(m_costAndPenalty == m_costAndPenaltyAfterSubGain-m_gain);
}

TEST_F(TestCostAndPenalty, OverloadOperatorSubtractAssignBetweenCostAndPenaltyAndGain)
{
    auto originCostAndPenalty = m_costAndPenalty;
    m_costAndPenalty -= m_gain;
    ASSERT_EQ(m_costAndPenaltyAfterSubGain, m_costAndPenalty);
    ASSERT_FALSE(m_costAndPenalty == originCostAndPenalty);
}

#if defined(TimeRelatedCons)
TEST_F(TestCostAndPenalty, IsTimeRelatedConstraintSatisfied)
{
    ASSERT_TRUE(m_costAndPenalty.isConstraintLegal(Constraint::duration));
    ASSERT_FALSE(m_costAndPenaltyAfterSubGain.isConstraintLegal(Constraint::duration));
}
#endif

#if defined(LoadRelatedCons)
TEST_F(TestCostAndPenalty, IsLoadRelatedConstraintSatisfied)
{
    ASSERT_FALSE(m_costAndPenalty.isConstraintLegal(Constraint::vehicleCap));
    ASSERT_FALSE(m_costAndPenaltyAfterSubGain.isConstraintLegal(Constraint::vehicleCap));
}
#endif

#if defined(RangeRelatedCons)
TEST_F(TestCostAndPenalty, IsRangeRelatedConstraintSatisfied)
{
    ASSERT_FALSE(m_costAndPenalty.isConstraintLegal(Constraint::range));
    ASSERT_TRUE(m_costAndPenaltyAfterSubGain.isConstraintLegal(Constraint::range));
}
#endif

TEST_F(TestCostAndPenalty, IsSolFeasible)
{
    ASSERT_FALSE(m_costAndPenalty.isFeasible());
    ASSERT_TRUE(m_feasibleCostAndPenalty.isFeasible());
}

TEST_F(TestCostAndPenalty, IsLegalCostAndPenalty)
{
    DisType legalCost = 5;
    DisType  illegalCost = -2;
    vector<DisType> legalPenalties(Constraint::num);
    vector<DisType> illegalPenalties(Constraint::num);
#if defined(Cons_vehicleCap)
    legalPenalties[Constraint::vehicleCap] = 7;
    illegalPenalties[Constraint::vehicleCap] = -2;
#endif
#if defined(Cons_duration)
    legalPenalties[Constraint::duration] = 0;
    illegalPenalties[Constraint::duration] = -2;
#endif
#if defined(Cons_depotCap)
    legalPenalties[Constraint::depotCap] = 5;
    illegalPenalties[Constraint::depotCap] = -2;
#endif
    CostAndPenalty tmp;
    assignCostAndPenalty(tmp, legalCost, legalPenalties);
    ASSERT_TRUE(tmp.isLegalCostAndPenalty());
    assignCostAndPenalty(tmp, legalCost, illegalPenalties);
    ASSERT_FALSE(tmp.isLegalCostAndPenalty());
    assignCostAndPenalty(tmp, illegalCost, legalPenalties);
    ASSERT_FALSE(tmp.isLegalCostAndPenalty());
    assignCostAndPenalty(tmp, illegalCost, illegalPenalties);
    ASSERT_FALSE(tmp.isLegalCostAndPenalty());
}